package com.example.neo4j.service;

import com.example.neo4j.repository.LabelRepository;
import org.neo4j.driver.Driver;
import org.neo4j.driver.Record;
import org.neo4j.driver.Result;
import org.neo4j.driver.Session;
import org.neo4j.driver.types.Node;
import org.neo4j.driver.types.Relationship;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.neo4j.core.Neo4jClient;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class GraphService {

    @Autowired
    private Driver driver;
    @Autowired
    private Neo4jClient neo4jClient;
    @Autowired
    private LabelRepository labelRepository;

    public Map<String, Object> getGraph() {
        Map<String, Object> resultMap = new HashMap<>();
        Set<Map<String, Object>> nodes = new HashSet<>();
        List<Map<String, Object>> edges = new ArrayList<>();

        try (Session session = driver.session()) {
            String cypher = "MATCH (n)-[r]->(m) RETURN n, r, m LIMIT 100";
            Result result = session.run(cypher);

            while (result.hasNext()) {
                Record record = result.next();
                Node n = record.get("n").asNode();
                Node m = record.get("m").asNode();
                Relationship r = record.get("r").asRelationship();

                Map<String, Object> nodeN = new HashMap<>(n.asMap());
                nodeN.put("id", n.id());
                nodes.add(nodeN);

                Map<String, Object> nodeM = new HashMap<>(m.asMap());
                nodeM.put("id", m.id());
                nodes.add(nodeM);

                Map<String, Object> edge = new HashMap<>(r.asMap());
                edge.put("from", r.startNodeId());
                edge.put("to", r.endNodeId());
                edge.put("label", r.type());
                edges.add(edge);
            }
        }

        resultMap.put("nodes", nodes);
        resultMap.put("edges", edges);
        return resultMap;
    }

    public Map<String, Object> getLabels() {
//        return getAllLabel1();
        return getAllLabel2();
    }

    private Map<String, Object> getAllLabel1() {
        Map<String, Object> resultMap = new HashMap<>();
        try(Session session = driver.session()) {
            List<String> labels = session.executeRead(tx -> {
                Result result = tx.run("CALL db.labels() YIELD label RETURN label");
                return result.list(record -> record.get("label").asString());
            });
            resultMap.put("labels", labels);
            return resultMap;
        }
    }
    private Map<String, Object> getAllLabel2() {
        Map<String, Object> resultMap = new HashMap<>();
        List<String> allLabels = labelRepository.findAllLabels();
        resultMap.put("labels", allLabels);
        return resultMap;
    }

    /**
     * 根据标签label获取对应的数据
     *
     * @param label 标签名称
     * @return 包含该标签的节点数据
     */
    public Map<String, Object> getDataByLabel(String label) {
        /**
         * 如果label为空，则返回所有标签的数据
         * 如果label不为空，则返回该标签的数据
         */
        return getDataByLabel1(label);
    }

    private Map<String, Object> getDataByLabel1(String label) {
        Map<String, Object> resultMap = new HashMap<>();
        String query = "";
        if (label== null || label.trim().isEmpty()){
            query = "MATCH (n) RETURN n";
        } else {
            query = String.format("MATCH (n:%s) RETURN n", label);
        }

//        return neo4jClient.query(query)
//                .fetch()
//                .all()
//                .stream()
//                .map(record -> {
//                    Node node = record.get("n").asNode();
//                    return node.asMap();
//                })
//                .collect(Collectors.toList());
        try (Session session = driver.session()) {
            Result result = session.run(query);
            List<Map<String, Object>> nodes = new ArrayList<>();
            while (result.hasNext()) {
                Record record = result.next();
                Node node = record.get("n").asNode();
                Map<String, Object> nodeMap = node.asMap();
//                nodeMap.put("label", node.labels());
                nodes.add(nodeMap);
            }
            resultMap.put("nodes", nodes);
            return resultMap;
        }
    }
}
